package com.matrix.im.netty.handler;

import com.matrix.common.enums.UserTypeEnum;
import com.matrix.im.service.SessionService;
import com.matrix.im.netty.session.SessionManager;
import com.matrix.im.protocol.MessageProto;
import com.matrix.im.utils.JwtUtil;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * @author 有点甜
 * @since 2025/8/6
 */
@Slf4j
@Component
@ChannelHandler.Sharable
@RequiredArgsConstructor
public class AuthHandler extends SimpleChannelInboundHandler<MessageProto.MessagePackage> {

    private final JwtUtil jwtUtil;
    private final SessionManager sessionManager;
    private final SessionService sessionService;

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, MessageProto.MessagePackage msg) {
        if (msg.getPackageType() != MessageProto.PackageType.AUTH_REQUEST) {
            if (ctx.channel().attr(SessionManager.USER_ID_KEY).get() == null) {
                log.warn("Non-auth packet received from unauthenticated client, closing connection: {}", ctx.channel().remoteAddress());
                ctx.close();
                return;
            }
            ctx.fireChannelRead(msg);
            return;
        }

        MessageProto.AuthRequest authRequest = msg.getAuthRequest();
        String token = authRequest.getToken();
        MessageProto.AuthResponse.Builder responseBuilder = MessageProto.AuthResponse.newBuilder();

        try {
            if (jwtUtil.isTokenExpired(token)) {
                throw new RuntimeException("Token has expired");
            }
            Long userId = jwtUtil.getUserIdFromAccessToken(token);
            String userTypeStr = jwtUtil.getUserTypeFromAccessToken(token);
            UserTypeEnum userType = UserTypeEnum.valueOf(userTypeStr);

            log.info("User authenticated successfully. userId: {}, userType: {}, channel: {}", userId, userType, ctx.channel().id());

            sessionManager.bindSession(userId, userType, ctx.channel());
            sessionService.userConnected(userId);

            responseBuilder.setSuccess(true).setMessage("Authentication successful");
            ctx.pipeline().remove(this);

        } catch (Exception e) {
            log.error("Authentication failed for client {}: {}", ctx.channel().remoteAddress(), e.getMessage());
            responseBuilder.setSuccess(false).setMessage("Authentication failed: " + e.getMessage());
            ctx.writeAndFlush(buildResponsePackage(responseBuilder.build())).addListener(future -> ctx.close());
            return;
        }

        ctx.writeAndFlush(buildResponsePackage(responseBuilder.build()));
    }

    private MessageProto.MessagePackage buildResponsePackage(MessageProto.AuthResponse authResponse) {
        return MessageProto.MessagePackage.newBuilder()
                .setPackageType(MessageProto.PackageType.AUTH_RESPONSE)
                .setAuthResponse(authResponse)
                .build();
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Long userId = ctx.channel().attr(SessionManager.USER_ID_KEY).get();
        if (userId != null) {
            sessionManager.unbindSession(ctx.channel());
            sessionService.userDisconnected(userId);
            log.info("User disconnected. userId: {}, channel: {}", userId, ctx.channel().id());
        }
        super.channelInactive(ctx);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        log.error("An error occurred in AuthHandler for channel {}: {}", ctx.channel().remoteAddress(), cause.getMessage());
        ctx.close();
    }
}
